/**
 * @class 资源收集器
 * @author Dusky Huang 圣子
 * @description 提供场景内资源可视化收集和设置
*/

import { AudioClip, Component, Enum, JsonAsset, Prefab, resources, SpriteFrame, _decorator } from 'cc';
import AudioManager from '../../managers/audio-manager';
import ResManager from '../../managers/res-manager';
import { tip } from "../../utils";
const { ccclass } = _decorator;
const res = ResManager.instance;
const aud = AudioManager.instance;

/** 资源类型 */
enum Type {
    NONE = 0,
    SHEET = 1,
    CONFIG = 2,
    BGM = 3,
    SFX = 4,
    PREFAB = 5,
}
function showPersist(this: ResItem) { return this.type != Type.NONE && this.type != Type.BGM; }
function showOverride(this: ResItem) { return this.type == Type.BGM || (this.type != Type.NONE && this.persist); }
function showDynamic(this: ResItem) { return this.type != Type.NONE && this.type != Type.PREFAB; } // TODO 远程加载Prefab
function showAutoLoad(this: ResItem) { return this.type != Type.NONE && this.dynamic; }
function showResDir(this: ResItem) { return this.type != Type.NONE && this.type != Type.BGM && this.dynamic; }
function showPathRes(this: ResItem) { return this.type === Type.BGM && this.dynamic; }
function showFrames(this: ResItem) { return this.type === Type.SHEET; }
function showJsons(this: ResItem) { return this.type === Type.CONFIG; }
function showPrefabs(this: ResItem) { return this.type === Type.PREFAB; }
function showBgm(this: ResItem) { return this.type === Type.BGM; }
function showSfx(this: ResItem) { return this.type === Type.SFX; }

@ccclass('cfw.ResItem')
export class ResItem {

    public static readonly Type: typeof Type = Type;
    @tip("资源类型", ["type", Enum(Type)]) public type: Type = Type.NONE;
    @tip("图集名称", showFrames) public sheetName: string = "";
    @tip("本地碎图", showFrames, ["type", [SpriteFrame]]) public frames: SpriteFrame[] = [];
    @tip("本地配置", showJsons, ["type", [JsonAsset]]) public jsons: JsonAsset[] = [];
    @tip("背景音乐", AudioClip, showBgm) public bgm: AudioClip = null;
    @tip("本地音效", showSfx, ["type", [AudioClip]]) public sfx: AudioClip[] = [];
    @tip("本地预制", showPrefabs, ["type", [Prefab]]) public prefabs: Prefab[] = [];
    @tip("场景共享（加入资源管理）", showPersist) public persist: boolean = true;
    @tip("覆盖已有", showOverride) public override: boolean = false;
    @tip("动态加载", showDynamic) public dynamic: boolean = false;
    @tip("自动加载", showAutoLoad) public autoLoad: boolean = true;
    @tip("目录路径", showResDir) public resDir: string = "";
    @tip("资源路径", showPathRes) public resPath: string = "";

}

@ccclass('cfw.ResCollector')
export class ResCollector extends Component {

    @tip("资源项", ["type", [ResItem]]) public items: ResItem[] = [];

    public hasSheet(name: string): boolean { return this._sheets.has(name); }
    public setSheet(name: string, sheet: SpriteFrame[]): void { this._sheets.set(name, sheet); }
    public getSheet(name: string): SpriteFrame[] { return this._sheets.get(name); }
    public getSheetFrame(name: string, key: string): SpriteFrame {
        const sheet = this._sheets.get(name);
        if (sheet) {
            for (let frame of sheet) { if (frame.name === key) return frame; }
        } return null;
    }

    public hasConfig(name: string): boolean { return this._configs.has(name); }
    public getConfig<T extends JsonObject>(name: string): T | null { return this._configs.get(name) as T; }
    public setConfig(name: string, config: JsonObject): void { this._configs.set(name, config); }
    public getConfigItem<T extends any>(name: string, key: string): T {
        const config = this._configs.get(name);
        return config ? config[key] as T : null;
    }

    public hasPrefab(name: string): boolean { return this._prefabs.has(name); }
    public getPrefab(name: string): Prefab | null { return this._prefabs.get(name); }
    public setPrefab(name: string, prefab: Prefab): void { this._prefabs.set(name, prefab); }

    protected onLoad(): void {
        if (!this.enabled) return;
        this.items.forEach(item => {
            const { type, persist, dynamic } = item;
            if (type === Type.NONE) return;
            const { autoLoad, override } = item;
            switch (type) {
                case Type.SHEET: {
                    const { frames, resDir, sheetName } = item,
                        loadRes = dynamic && autoLoad && !!resDir;
                    frames.clean();
                    this.setSheet(sheetName, [...frames]);
                    if (persist) res.addSheetFrames(sheetName, ...frames);
                    if (!loadRes) return;
                    resources.loadDir(resDir, SpriteFrame, (err, frames) => {
                        if (err) console.warn(err);
                        else this.getSheet(sheetName).push(...frames);
                    });
                    if (persist) res.loadResSheetByDir(resDir, sheetName, override);
                }; break;
                case Type.CONFIG: {
                    const { jsons, resDir } = item,
                        loadRes = dynamic && autoLoad && !!resDir;
                    jsons.clean();
                    jsons.forEach(json => this.setConfig(json.name, json.json));
                    if (persist) jsons.forEach(json => res.addConfig(json.name, json.json, override));
                    if (!loadRes) return;
                    resources.loadDir(resDir, JsonAsset, (err, jsons) => {
                        if (err) console.warn(err);
                        else jsons.forEach(json => this.setConfig(json.name, json.json));
                    });
                    if (persist) loadRes && res.loadResConfigsByDir(resDir, override);
                }; break;
                case Type.BGM: {
                    const { bgm, resPath } = item,
                        loadRes = dynamic && autoLoad && !!resPath;
                    if (bgm) { if (!aud.bgm || override) aud.bgm = bgm; }
                    else if (loadRes) resources.load(resPath, AudioClip, (err, clip) => {
                        if (err) console.warn(err);
                        else if (!aud.bgm || override) aud.bgm = clip;
                    });
                }; break;
                case Type.SFX: {
                    const { sfx, resDir } = item,
                        loadRes = dynamic && autoLoad && !!resDir;
                    sfx.clean();
                    if (persist) {
                        if (notEmptyArr(sfx)) aud.addSfx(...sfx);
                        loadRes && resources.loadDir(resDir, AudioClip, (err, clips) => {
                            if (err) console.warn(err);
                            else aud.addSfx(...sfx);
                        });
                    } else {
                        loadRes && resources.loadDir(resDir, AudioClip);
                    }
                }; break;
                case Type.PREFAB: {
                    const { prefabs, persist, override } = item;
                    prefabs.clean();
                    prefabs.forEach(pfb => this.setPrefab(pfb.name, pfb));
                    if (persist) prefabs.forEach(pfb => res.addPrefab(pfb.name, pfb, override));
                }; break;
            }
        });
    }

    private _configs: Map<string, JsonObject> = new Map;
    private _prefabs: Map<string, Prefab> = new Map;
    private _sheets: Map<string, SpriteFrame[]> = new Map;
}